import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosStatic } from 'axios';
import { HttpConfigSource } from './config';

export interface HttpMethod {
  [propName: string]: string;

  GET: 'get';
  POST: 'post';
  PUT: 'put';
  PATCH: 'patch';
  DELETE: 'delete';
  OPTIONS: 'options';
  HEAD: 'head';
}

export interface HttpConfig extends AxiosRequestConfig {
  id: symbol;
}

export interface InstanceCallback {
  (instance: AxiosInstance): void;
}

export interface Http {
  development: boolean;

  method: HttpMethod;

  create(config?: HttpConfig, callback?: InstanceCallback): symbol;

  destroy(id: symbol): boolean;

  request<T, R = AxiosResponse<T>>(config: HttpConfig): Promise<R>;

  all(configs: HttpConfig[]): unknown;

  raw(): AxiosStatic;

  showMemory(): void;
}

const instances: Map<symbol, AxiosInstance> = new Map();

export const http: Http = {
  development: false,

  method: {
    GET: 'get',
    POST: 'post',
    PUT: 'put',
    PATCH: 'patch',
    DELETE: 'delete',
    OPTIONS: 'options',
    HEAD: 'head',
  },

  create(config?: HttpConfig, callback?: InstanceCallback): symbol {
    const httpConfig = HttpConfigSource.merge(config);
    const instance = axios.create(config);
    if (callback) {
      callback(instance);
    }
    instances.set(httpConfig.id, instance);
    if (this.development) {
      this.showMemory();
    }
    return httpConfig.id;
  },

  destroy(id: symbol): boolean {
    const res = instances.delete(id);
    if (this.development) {
      this.showMemory();
    }
    return res;
  },

  request<T, R = AxiosResponse<T>>(config: HttpConfig): Promise<R> {
    let instance: AxiosInstance | AxiosStatic = axios;
    if (instances.get(config.id)) {
      instance = instances.get(config.id) || axios;
    } else {
      console.warn(`unable to find the value of ${config.id.toString} in instances.`);
    }
    return instance.request(config);
  },

  all(configs: HttpConfig[]) {
    return Promise.allSettled(configs.map(config => this.request(config)));
  },

  raw(): AxiosStatic {
    return axios;
  },

  showMemory(): void {
    if (instances.size === 0) {
      console.warn('no memory');
    } else {
      console.warn('start show memory');
      instances.forEach((value: AxiosInstance, key: symbol) => {
        console.log(key.toString());
      });
      console.warn('end show memory');
    }
  },
};
